'use client';

import React, { Component, ErrorInfo, ReactNode } from 'react';
import { ErrorFallback } from './error-fallback';

interface ErrorBoundaryProps {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
  resetKeys?: any[];
}

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

/**
 * 错误边界组件
 * 
 * 捕获子组件中的 JavaScript 错误，记录错误并显示回退 UI
 * 
 * @example
 * ```tsx
 * <ErrorBoundary>
 *   <ComponentThatMightError />
 * </ErrorBoundary>
 * ```
 */
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = {
      hasError: false,
      error: null
    };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    // 更新 state，下次渲染将显示回退 UI
    return {
      hasError: true,
      error
    };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    // 记录错误信息
    console.error('ErrorBoundary 捕获到错误:', error, errorInfo);
    
    // 如果提供了自定义错误处理函数，则调用它
    if (this.props.onError) {
      this.props.onError(error, errorInfo);
    }
  }

  componentDidUpdate(prevProps: ErrorBoundaryProps): void {
    // 如果提供了 resetKeys，并且它们发生了变化，则重置错误状态
    if (this.props.resetKeys && this.state.hasError) {
      const hasResetKeysChanged = this.props.resetKeys.some(
        (key, index) => key !== (prevProps.resetKeys || [])[index]
      );

      if (hasResetKeysChanged) {
        this.reset();
      }
    }
  }

  reset = (): void => {
    this.setState({
      hasError: false,
      error: null
    });
  };

  render(): ReactNode {
    if (this.state.hasError) {
      // 如果提供了自定义回退 UI，则使用它
      if (this.props.fallback) {
        return this.props.fallback;
      }
      
      // 否则使用默认回退组件
      return (
        <ErrorFallback
          error={this.state.error || new Error('发生未知错误')}
          resetErrorBoundary={this.reset}
        />
      );
    }

    return this.props.children;
  }
}

/**
 * 错误边界HOC
 * 
 * 用于包装组件，提供错误边界功能
 * 
 * @example
 * ```tsx
 * const SafeComponent = withErrorBoundary(UnsafeComponent);
 * ```
 */
export function withErrorBoundary<P extends object>(
  Component: React.ComponentType<P>,
  errorBoundaryProps: Omit<ErrorBoundaryProps, 'children'> = {}
): React.FC<P> {
  const displayName = Component.displayName || Component.name || 'Component';
  
  const ComponentWithErrorBoundary = (props: P) => {
    return (
      <ErrorBoundary {...errorBoundaryProps}>
        <Component {...props} />
      </ErrorBoundary>
    );
  };
  
  ComponentWithErrorBoundary.displayName = `withErrorBoundary(${displayName})`;
  
  return ComponentWithErrorBoundary;
} 